#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/random.h>
#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/highmem.h>
#include <linux/freezer.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>

struct vivid_dev {
    struct v4l2_device v4l2_dev;
    struct video_device vivi_device;
    spinlock_t vivi_queue_slock;
    struct videobuf_queue vivi_vb_vidqueue; // 队列操作1：定义队列
    struct list_head vivi_devlist;
};

/* timer设备结构体 */
struct timer_dev {
	struct timer_list timer; /* 定义一个定时器 */
    spinlock_t timer_lock; /* 定义自旋锁 */
};

static struct timer_dev vivi_timer;
static LIST_HEAD(vivi_devlist); /* 本地列表，用于exit时check*/
static struct v4l2_format myvivi_format;
static struct list_head vivi_vb_local_queue;

#include "fillbuf.c"

/*
 * description: APP调用ioctl VIDIOC_REQBUFS时会导致此函数被调用,它重新调整count和size
 * parameters:
 *              count: number of buffers
 *              size: The size of each frame of data
 *
 */
static int myvivi_buf_setup(struct videobuf_queue *q,
                    unsigned int *count, unsigned int * size)
{
    if (*count == 0)
        *count = 2;

    *size = myvivi_format.fmt.pix.sizeimage;
    printk("count:%d, size:%d\n", *count, *size);

    return 0;
}


/* description: APP调用ioctlVIDIOC_QBUF时导致此函数被调用,
 *              它会填充video_buffer结构体并调用videobuf_iolock来分配内存
 * parameters:
 *
 */
static int myvivi_buf_prepare(struct videobuf_queue *q,
                    struct videobuf_buffer *vb, enum v4l2_field field)
{
    /* 0. 设置videobuf*/
    vb->size = myvivi_format.fmt.pix.sizeimage;
    vb->bytesperline = myvivi_format.fmt.pix.bytesperline;
    vb->width = myvivi_format.fmt.pix.width;
    vb->height = myvivi_format.fmt.pix.height;
    vb->field = field;

    /* 1. 做些准备工作*/
    myvivi_precalculate_bars(0);
#if 0
    /* 2. 调用videobuf_iolock为类型为V4L2_MEMORY_USERPTR的videobuf分配内存 */
    if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {

        err = videobuf_iolock(q,&buf->vb, NULL);
        if (err)
            goto oops;
    }
#endif

    /* 3. 设置状态*/
    vb->state = VIDEOBUF_PREPARED;

    return 0;
}

/* APP调用ioctlVIDIOC_QBUF时:
 * 1. 先调用buf_prepare进行一些准备工作
 * 2. 把buf放入队列
 * 3. 调用buf_queue(起通知作用)
 */
static void myvivi_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
    vb->state = VIDEOBUF_QUEUED;

    /*
     * 把videobuf放入本地一个队列中，
     * 定时器处理函数就可以从本地队列取出videobuf
     */
    list_add_tail(&vb->queue, &vivi_vb_local_queue);
}

/*
 *
 * APP不再使用队列时，用它来释放内存
 */
static void myvivi_buf_releae(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
    videobuf_vmalloc_free(vb);
    vb->state = VIDEOBUF_NEEDS_INIT;
}

static const struct videobuf_queue_ops myvivi_video_qops = {
    .buf_setup = myvivi_buf_setup,  // 计算大小
    .buf_prepare = myvivi_buf_prepare,
    .buf_queue = myvivi_buf_queue,
    .buf_release = myvivi_buf_releae,
};

static void myvivi_dev_release(struct v4l2_device *v4l2_dev)
{
	struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev);

	v4l2_device_unregister(&dev->v4l2_dev);
	// kfree(dev);
}

static int myvivi_open(struct file *file)
{
    struct vivid_dev *dev = video_drvdata(file);
    // 队列操作2：初始化
    videobuf_queue_vmalloc_init(&dev->vivi_vb_vidqueue, &myvivi_video_qops,
                            NULL,
                            &dev->vivi_queue_slock,
                            V4L2_BUF_TYPE_VIDEO_CAPTURE,
                            V4L2_FIELD_INTERLACED,
                            sizeof(struct videobuf_buffer), // buffer头部的大小
                            NULL,
                            NULL);

    vivi_timer.timer.expires = jiffies + msecs_to_jiffies(30);
    add_timer(&vivi_timer.timer);

    return 0;
}

static int myvivi_close(struct file *file)
{
    int ret;
    struct vivid_dev *dev = video_drvdata(file);

    del_timer(&vivi_timer.timer);
    videobuf_stop(&dev->vivi_vb_vidqueue);
    ret = videobuf_mmap_free(&dev->vivi_vb_vidqueue);
    return ret;
}

static int myvivi_mmap(struct file *file, struct vm_area_struct *vma)
{
    int ret;
    struct vivid_dev *dev = video_drvdata(file);

    ret = videobuf_mmap_mapper(&dev->vivi_vb_vidqueue, vma);

    return ret;
}

static unsigned int myvivi_poll(struct file *file, struct poll_table_struct *wait)
{
    struct vivid_dev *dev = video_drvdata(file);
    return videobuf_poll_stream(file, &dev->vivi_vb_vidqueue, wait);
}

static const struct v4l2_file_operations myvivi_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = video_ioctl2,
    .open = myvivi_open,
    .release = myvivi_close,
    .mmap = myvivi_mmap,
    .poll = myvivi_poll,
};


static int myvivi_vidioc_querycap(struct file *file, void *priv,
                            struct v4l2_capability *cap)
{
    strcpy(cap->driver, "myvivi");
    strcpy(cap->card, "myvivi");
    cap->version = 0x0001;
    cap->device_caps  = V4L2_CAP_VIDEO_CAPTURE |
                            V4L2_CAP_STREAMING |
                            V4L2_CAP_DEVICE_CAPS;

    cap->capabilities =	V4L2_CAP_VIDEO_CAPTURE |
                        V4L2_CAP_STREAMING |
                        V4L2_CAP_DEVICE_CAPS;

    return 0;
}

// 列举摄像头所支持的格式
static int myvivi_vidioc_enmu_fmt_vid_cap(struct file *file, void *priv,
                                    struct v4l2_fmtdesc *f)
{
    if (f->index >= 1)
        return -EINVAL;

    strcpy(f->description, "4:2:2, packed, YUYV");
    f->pixelformat = V4L2_PIX_FMT_YUYV;

    return 0;
}

// 返回当前所使用的格式
static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f )
{

    memcpy(f, &myvivi_format, sizeof(myvivi_format));

    return 0;
}

// 测试驱动程序是否支持某种格式
static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
{
    enum v4l2_field field;

    if( f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
        return -EINVAL;
    }

    field = f->fmt.pix.field;

    if (field == V4L2_FIELD_ANY) {
        field = V4L2_FIELD_INTERLACED;
    } else if (V4L2_FIELD_INTERLACED != field) {
        return -EINVAL;
    }

    f->fmt.pix.field = field;

    v4l_bound_align_image(&f->fmt.pix.width, 48, 1024, 2,
			      &f->fmt.pix.height, 32, 768, 0, 0);

    // bytesperline是每行字节数，16是每个像素用16位表示，
    //右移3位相当于除以2^3，也就是单位是字节。
	f->fmt.pix.bytesperline =
		(f->fmt.pix.width * 16) >> 3;
	f->fmt.pix.sizeimage =
		f->fmt.pix.height * f->fmt.pix.bytesperline;

    printk("[%s:%d] bytesperline:%d, sizeimage:%d, --->\n", __func__, __LINE__,
                f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);

    return 0;

}

static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                            struct v4l2_format *f)
{
    int ret;

    ret = myvivi_vidioc_try_fmt_vid_cap(file, priv, f);
    if (ret < 0) {
        goto fail_ret;
    }

    memcpy(&myvivi_format, f, sizeof(myvivi_format));

    return 0;

fail_ret:
    return ret;
}

static int myvivi_vidioc_reqbufs(struct file *file, void *priv,
                        struct v4l2_requestbuffers *p)
{
    struct vivid_dev *dev = video_drvdata(file);
    return (videobuf_reqbufs(&dev->vivi_vb_vidqueue, p));
}

static int myvivi_vidioc_querybuf(struct file *file, void *priv,
                        struct v4l2_buffer *p)
{
    struct vivid_dev *dev = video_drvdata(file);
    return (videobuf_querybuf(&dev->vivi_vb_vidqueue, p));
}

static int myvivi_vidioc_qbuf(struct file *file, void *priv,
                        struct v4l2_buffer *p)
{
    struct vivid_dev *dev = video_drvdata(file);
    return (videobuf_qbuf(&dev->vivi_vb_vidqueue, p));
}

static int myvivi_vidioc_dqbuf(struct file *file, void *priv,
                        struct v4l2_buffer *p)
{
    struct vivid_dev *dev = video_drvdata(file);
    return (videobuf_dqbuf(&dev->vivi_vb_vidqueue, p, file->f_flags & O_NONBLOCK));
}

static int myvivi_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
    struct vivid_dev *dev = video_drvdata(file);
    return (videobuf_streamon(&dev->vivi_vb_vidqueue));
}

static int myvivi_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
    struct vivid_dev *dev = video_drvdata(file);
    return (videobuf_streamoff(&dev->vivi_vb_vidqueue));
}

static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {

    .vidioc_querycap = myvivi_vidioc_querycap,

    .vidioc_enum_fmt_vid_cap = myvivi_vidioc_enmu_fmt_vid_cap,
    .vidioc_g_fmt_vid_cap = myvivi_vidioc_g_fmt_vid_cap,
    .vidioc_try_fmt_vid_cap = myvivi_vidioc_try_fmt_vid_cap,
    .vidioc_s_fmt_vid_cap     = myvivi_vidioc_s_fmt_vid_cap,

    // buffer操作：申请，查询，放入队列，从队列取出
    .vidioc_reqbufs       = myvivi_vidioc_reqbufs,
	.vidioc_querybuf      = myvivi_vidioc_querybuf,
	.vidioc_qbuf          = myvivi_vidioc_qbuf,
	.vidioc_dqbuf         = myvivi_vidioc_dqbuf,
#if 0
    .vidioc_s_std         = myvivi_vidioc_s_std,
	.vidioc_enum_input    = myvivi_vidioc_enum_input,
	.vidioc_g_input       = myvivi_vidioc_g_input,
	.vidioc_s_input       = myvivi_vidioc_s_input,
#endif
	.vidioc_streamon      = myvivi_vidioc_streamon,
	.vidioc_streamoff     = myvivi_vidioc_streamoff,

};

static void timer_function(struct timer_list *unused)
{
    struct videobuf_buffer *vb;
    void *vbuf;
    struct timeval ts;
    // printk("[%s %d] enter\n", __func__, __LINE__);
    // struct vivid_dev *dev = (struct vivid_dev*)data;
    /*
     * 1.构造数据：从队列头部取出第一个videobuf, 填充数据
     */

    /* 1.1 从本地队列取出第1个videobuf */
    if(list_empty(&vivi_vb_local_queue)) {
        // check queue 是否为空，为空goto
        goto out;
    }
    vb = list_entry(vivi_vb_local_queue.next,
                struct videobuf_buffer, queue);// 指针，类型，成员，变量

    /* nobody is waiting on this buffer, return*/
    if(!waitqueue_active(&vb->done)) {
        goto out;
    }

    /* 1.2 填充数据 */
    vbuf = videobuf_to_vmalloc(vb);
    // memset(vbuf, 0, vb->size);
    myvivi_fillbuff(vb);
    vb->field_count++;
    do_gettimeofday(&ts);
    vb->ts = ts;
    vb->state = VIDEOBUF_DONE;

    /* 1.3 把videobuf从本地队列中删除*/
    list_del(&vb->queue);

    /* 2. 唤醒进程：唤醒videobuf->done上的进程*/
    wake_up(&vb->done);


out:
    /*
     * 3.修改timer的超时时间:30fps：1s有30帧数据。 每1/30s产生一帧数据
     *   这里设置timer的时间为30ms
     */
    mod_timer(&vivi_timer.timer, jiffies + msecs_to_jiffies(30));
}

static int __init myvivi_init(void)
{
    int ret;

    struct vivid_dev *dev;
    static struct video_device *myvivi_device;

    /* allocate main vivid state structure */
	dev = kzalloc(sizeof(struct vivid_dev), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;

    /* register v4l2_device */
	// snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
	// 		"%s-%03d", "myvivi", 1);
	// ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
	// if (ret) {
	// 	kfree(dev);
	// 	return ret;
	// }

    spin_lock_init(&dev->vivi_queue_slock);
	dev->v4l2_dev.release = myvivi_dev_release;

    myvivi_device = &(dev->vivi_device);
    myvivi_device->release = video_device_release_empty;
    myvivi_device->fops = &myvivi_fops;
    myvivi_device->ioctl_ops = &myvivi_ioctl_ops;
    myvivi_device->v4l2_dev = &dev->v4l2_dev;
    snprintf(myvivi_device->name,
            sizeof(myvivi_device->name), "vivid-%03d-vid-cap", 1);

    video_set_drvdata(myvivi_device, dev);
    ret = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1);
    if (ret < 0) {
        goto rel_dev;
    }
    list_add_tail(&dev->vivi_devlist, &vivi_devlist);

    /* 初始化timer，设置定时器处理函数,还未设置周期，所以没激活定时器
     * 用定时器产生数据并唤醒进程
     */
    // init_timer(&vivi_timer.timer);
    timer_setup(&vivi_timer.timer, timer_function, 0);
    // vivi_timer.timer.function = timer_function;
    // dev->timer.data = (unsigned long)dev;
    INIT_LIST_HEAD(&vivi_vb_local_queue);

    return 0;
rel_dev:
    video_device_release(myvivi_device);

    return  ret;
}

static void __exit myvivi_exit(void)
{
    struct vivid_dev *dev;
	struct list_head *list;

	while (!list_empty(&vivi_devlist)) {
		list = vivi_devlist.next;
		list_del(list);
		dev = list_entry(list, struct vivid_dev, vivi_devlist);

		// v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
		// 	video_device_node_name(dev->vivi_device));
		video_unregister_device(&dev->vivi_device);
		v4l2_device_unregister(&dev->v4l2_dev);
		kfree(dev);
	}
}

module_init(myvivi_init);
module_exit(myvivi_exit);


MODULE_LICENSE("GPL");
